Visualización de datos

Residencia de Epidemiología

Visualización

“Un simple gráfico ha brindado más información a la mente del analista de datos que cualquier otro dispositivo”. — John Tukey

Objetivos de la visualización




  • Análisis exploratorio: descubrir y describir patrones en los datos (parte del Análisis Exploratorio de Datos - EDA)


  • Presentación y comunicación: capacidad de transmitir el mensaje de forma clara y atractiva.

Señales visuales

Gramática de gráficos

De manera similar a la gramática lingüística, “La gramática de gráficos” define un conjunto de reglas para construir gráficos estadísticos combinando diferentes tipos de capas.

Esta gramática fue creada por Leland Wilkinson (2005, The Grammar of Graphics (Statistics and Computing). Secaucus, NJ, USA: Springer-Verlag New York, Inc.)

ggplot2 es un paquete que se autodefine como librería para “crear elegantes visualizaciones de datos usando una gramática de gráficos”

El paquete propone un sistema que se basa en la idea que cualquier gráfico se puede construir usando tres componentes básicos:

Esquema gráfico ggplot2


  • Datos con estructura “ordenada”

  • Mapeo estético (aesthetic) de los datos

  • Objetos geométricos que dan nombre al tipo de gráfico

  • Coordenadas que organizan los objetos geométricos

  • Escalas (scale) definen el rango de valores de las estéticas

  • Facetas que agrupan en subgráficos

Las geometrias definen el tipo de gráfico

El esquema básico de un gráfico


<DATOS> |>  
  ggplot(mapping = aes(<MAPEO>)) +
  <GEOM_FUNCION>()

Las capas posteriores son opcionales. Algunas de ellas son:

[dataframe] |>  
  ggplot(mapping = aes(x = [x-varible],
                       y = [y-variable])) +
  geom_xxx() +
  scale_x_...() +
  scale_y_...() +
  scale_fill_...() +
  otras capas más

Construcción de gráfico paso a paso

La idea en esta presentación es mostrar paso a paso la construcción de un gráfico.

  • Partiremos del objetivo deseado, en este caso un tipo de gráfico que ya existe, pero podría ser algún otro ejemplo o un bosquejo de lo imaginado garabateado en un papel.

  • El segundo paso es determinar cuales son las variables necesarias para construir ese gráfico y donde se mapean.

  • El tercer paso es trabajar con los datos y transformarlos para que estén preparados para graficar.

  • El cuarto es el gráfico en si mismo con todas las personalizaciones de sus partes.

Gráfico objetivo


Queremos construir un gráfico similar a este

Variables participantes


  • El análisis del modelo indica que hay dos capas geométricas simultáneas: una de barras y otra de líneas.

  • También hay dos ejes y (a izquierda Números de casos y a derecha Tasas por 100.000) y un eje x (Años)

  • Las variables participantes son tres:

    • el número de casos que mapea en y1,
    • las tasas que mapean en y2 y
    • los años de la tendencia que se ubican en x.

Lectura y adecuación de los datos


Importamos datos de la tendencia de TB de la provincia de Santa Fe entre los años 1980 y 2008

library(tidyverse)
library(readxl)

tendencia1 <- read_excel("datos/Tendencia tb 1980 2008 por provincia.xls", 
                        sheet = "SFE", range = "A6:A34", col_names = F) |> 
   rename("Año" = ...1)

tendencia2 <- read_excel("datos/Tendencia tb 1980 2008 por provincia.xls", 
                        sheet = "SFE", range = "D6:E34", col_names = F) |> 
  rename("Casos" = ...1,
         "Tasa" = ...2)

tendencia <- tendencia1 |> 
  bind_cols(tendencia2) 

Lectura y adecuación de los datos


El dataframe necesario para este gráfico nos quedaría así:

# A tibble: 29 × 3
     Año Casos  Tasa
   <dbl> <dbl> <dbl>
 1  1980   904  36.2
 2  1981   790  31.2
 3  1982   853  33.3
 4  1983   802  30.9
 5  1984   794  30.2
 6  1985   669  25.1
 7  1986   659  24.5
 8  1987   516  18.9
 9  1988   722  26.2
10  1989   661  23.7
# ℹ 19 more rows

Primer gráfico


Iniciamos con el gráfico de barras. Mapeamos x e y, indicamos color de relleno e identidad porque las barras se van a construir con el conteo previo (datos agregados)


tendencia |> 
  ggplot(aes(x = Año, y = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue")

Primer gráfico

Capa de texto


Agregamos capa de texto con los valores de los casos.

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue") +
  geom_text()

Capa de texto

Capa de texto


Adecuamos los valores en las barras.

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue") +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white")

Capa de texto

Eje x


Configuramos eje x

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue") +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white") +
  scale_x_continuous(name = "Años", breaks = seq(1980, 2008, 1))

Eje x

Eje x


Personalizamos mejor el eje x (ángulo de las etiquetas, etc)

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue") +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white") +
  scale_x_continuous(name = "Años", breaks = seq(1980, 2008, 1)) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 11))

Eje x

Eje y


Configuramos el eje y asociado a las barras

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue") +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white") +
  scale_x_continuous(name = "Años", breaks = seq(1980, 2008, 1)) +
  scale_y_continuous(name = "Número de casos", breaks = seq(0, 1000, 200)) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 11),
        axis.text.y = element_text(size = 11))

Eje y

Segundo gráfico


Incoporamos el segundo gráfico o segunda capa geométrica. Utilizamos una constante para llevar los valores al mismo orden de los valores de las barras.

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue") +
  geom_line(aes(y = Tasa*35), color = "red", linewidth = 1.5) +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white") +
  scale_x_continuous(name = "Años", 
                     breaks = seq(1980, 2008, 1)) +
  scale_y_continuous(name = "Número de casos", 
                     breaks = seq(0, 1000, 200),
                     sec.axis = sec_axis(~./35, 
                                         name = "Tasa")) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, size = 11),
        axis.text.y = element_text(size = 11))

Segundo gráfico

Etiquetas del segundo gráfico


Agregamos etiquetas asociadas a la capa geométrica line

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos)) +
  geom_bar(stat = "identity", 
           fill = "royalblue") +
  geom_line(aes(y = Tasa*35), color = "red", linewidth = 1.5) +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white") +
  geom_text(aes(label = round(Tasa,1), y = Tasa*35), angle = 45, hjust = -0.2, vjust = -0.5) +
  scale_x_continuous(name = "Años",
                     breaks = seq(1980, 2008, 1)) +
  scale_y_continuous(name = "Número de casos", 
                     breaks = seq(0, 1400, 200),
                     limits = c(0,1400),
                     sec.axis = sec_axis(~./35, 
                                         name = "Tasas por 100.000", 
                                         breaks = seq(0, 60, 10))) +
  theme(axis.text.x = element_text(angle = 45, 
                                   hjust = 1, 
                                   size = 11),
        axis.text.y = element_text(size = 11))

Etiquetas del segundo gráfico

Leyendas


Al igual que el modelo del gráfico, sumamos dentro del mismo las leyendas

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos, fill = "Número de casos")) +
  geom_bar(stat = "identity") +
  geom_line(aes(y = Tasa*35, color = "Tasas por 100.000"), linewidth = 1.5) +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white") +
  geom_text(aes(label = round(Tasa,1), y = Tasa*35), angle = 45, hjust = -0.2, vjust = -0.5) +
  scale_x_continuous(name = "Años",
                     breaks = seq(1980, 2008, 1)) +
  scale_y_continuous(name = "Número de casos", 
                     breaks = seq(0, 1400, 200),
                     limits = c(0,1400),
                     sec.axis = sec_axis(~./35, 
                                         name = "Tasas por 100.000", 
                                         breaks = seq(0, 60, 10))) +
  scale_fill_manual(values = "royalblue") +
  scale_color_manual(values = "red") +
  theme(axis.text.x = element_text(angle = 45, 
                                   hjust = 1, 
                                   size = 11),
        axis.text.y = element_text(size = 11),
        legend.position = "inside",
        legend.position.inside = c(0.8, 0.8),
        legend.background = element_rect(fill = "transparent"),
        legend.text.position = "right",
        legend.title=element_blank()) +
  guides(color = guide_legend(order = 0),
         fill = guide_legend(order = 1))

Leyendas

Título y demás


Por último, definimos estructura de titulo, subtitulo y pie de gráfico (habría que reemplazarlos por la definición de cada uno).

tendencia |> 
  ggplot(aes(x = Año, y = Casos, label = Casos, fill = "Número de casos")) +
  geom_bar(stat = "identity") +
  geom_line(aes(y = Tasa*35, color = "Tasas por 100.000"), linewidth = 1.5) +
  geom_text(hjust = 1.2, vjust = 0.5, angle = 90, color = "white") +
  geom_text(aes(label = round(Tasa,1), y = Tasa*35), angle = 45, hjust = -0.2, vjust = -0.5) +
  scale_x_continuous(name = "Años",
                     breaks = seq(1980, 2008, 1)) +
  scale_y_continuous(name = "Número de casos", 
                     breaks = seq(0, 1400, 200),
                     limits = c(0,1400),
                     sec.axis = sec_axis(~./35, 
                                         name = "Tasas por 100.000", 
                                         breaks = seq(0, 60, 10))) +
  scale_fill_manual(values = "royalblue") +
  scale_color_manual(values = "red") +
  labs(title = "Titulo", subtitle = "subtitulo", caption = "pie de grafico" ) +
  theme(axis.text.x = element_text(angle = 45, 
                                   hjust = 1, 
                                   size = 11),
        axis.text.y = element_text(size = 11),
        legend.position = "inside",
        legend.position.inside = c(0.8, 0.8),
        legend.background = element_rect(fill = "transparent"),
        legend.text.position = "right",
        legend.title=element_blank()) +
  guides(color = guide_legend(order = 0),
         fill = guide_legend(order = 1))

Título y demás

Exportar gráficos de ggplot2

  • Desde el panel Plot de RStudio

  • En formatos conocidos como JPG, PNG, PDF, etc

  • Mayor control con la función ggsave()

ggsave(filename,               # nombre del archivo
  plot = last_plot(),          # nombre del objeto gráfico
  device = NULL,               # formato de salida "jpeg", "png", "tiff", "pdf", etc
  width = NA,                  # ancho en unidades de units
  height = NA,                 # alto en unidades de units
  units = c("in", "cm", "mm"), # unidades de medidas
  dpi = 300)                   # resolución de salida en dpi

Gráficos que no hacen lo que quiero


Muchas veces nos encontramos con la situación de crear un gráfico que se ve bien en el panel Plot de RStudio y cuando lo exportamos o incluímos dentro de un documento de Quarto el tamaño de sus partes (elementos geométricos, textos, leyendas, etc) se achican o agrandan de tal forma que el producto final es feo y/o desproporcionado.


La respuesta a estos problemas tienen que ver con dos conceptos que debemos entender y poder controlar: el tamaño y la resolución.

Pantalla

Repasemos algunos conceptos de la imagen en pantalla:

  • La pantalla es una matriz de pixeles. Todas las imagenes que vemos en ella estan compuesta por ellos.
  • La que estoy utilizando ahora, para hacer esta presentación, tiene una dimensión de 24 pulgadas con un ratio de aspecto 16/10 (20 x 12,5 pulgadas)
  • La resolución es de 1920 x 1200, es decir tiene 1920 píxeles de ancho y 1200 píxeles de alto.
  • El número de píxeles por pulgada es de 96 (ppi).

Pantalla


Hagamos algunos calculos para ver si todo está en orden:


  • ancho: 1920 (px) / 96 (px/in) = 20 pulgadas

  • alto: 1200 (px) / 96 (px/in) = 12,5 pulgadas

  • relación: 1920 / 1200 = 20 / 12,5 = 16/10

Archivos de imagen


Las imágenes que se crean con ggplot2, por ejemplo, están constituidas por pixeles.

En estas imágenes también se relacionan las dimensiones y las resoluciones.

Para pasar de la dimensión en pulgadas a una cantidad de puntos, por ejemplo al exportar con ggsave() se utiliza la cantidad de puntos por pulgadas (dpi).

Si creamos un gráfico de 12 x 10 pulgadas a 300 dpi, el archivo será una matriz de (12 * 300) x (10 * 300) = 3600 x 3000 puntos.

Cuando abrimos el archivo en nuestra computadora, cada punto representa un pixel, lo que da una imagen de 3600 x 3000 px de resolución.

Archivos de imagen


Imaginemos que tenemos una imagen de 500x500px. Así se vería cada una, con un zoom del 100%, en un monitor de 1080p (1920x1080px) y en otro 4K (3840×2160), ambos del mismo tamaño.

Experimento práctico


Hagamos un pequeño experimento con un gráfico sencillo basado en los pinguinos.

library(tidyverse)
library(datos)

pinguinos |>
    ggplot(aes(largo_pico_mm, 
               alto_pico_mm, 
               color = especie)) +
    geom_point()

Experimento práctico


ggsave("prueba.png", 
       width = 10, 
       height = 5, 
       dpi = 300)

Si calculamos, la salida se almacena como:

  • ancho: 10 pulgadas * 300 dpi = 3000 px

  • alto: 5 pulgadas * 300 dpi = 1500 px

Ahora si en lugar de guardar el gráfico solo lo visualizaramos en el monitor:

  • ancho: 10 pulgadas * 96 dpi = 960 px

  • alto: 5 pulgadas * 96 dpi = 480 px

Experimento práctico


  • La relación entre el tamaño de la pantalla y el tamaño físico depende del dpi (puntos por pulgada) del dispositivo gráfico, que es 96 por defecto para la pantalla.
  • Luego, si guardamos el gráfico con un dpi mayor o menor, el archivo se guarda como una matriz de puntos usando el nuevo dpi y el tamaño en pulgadas.
  • Entonces, cuando se abre en la computadora, la dimensión del tamaño en píxeles es diferente.

Si visualizamos un gráfico con muchos más pixeles que otro en el mismo espacio (nuestra pantalla) los elementos que lo componen se van a ver siempre más pequeños.

¿Por qué es importante esta diferencia?




Importa porque algunos elementos del gráfico se ajustan al espacio disponible, y algunos son fijos y medidos en su dimensión real (cm, mm o pulgadas) como las fuentes de los textos, creando una distorsión al cambiar la dimensión del gráfico o su resolución.

Tamaño predeterminado en Quarto

Formato Por defecto
Por defecto 7 x 5
Diapositivas HTML 9,5 x 6,5
Diapositivas HTML (reveal.js) 9 x 5
PDF 5,5 x 3,5
Diapositivas PDF (Beamer) 10x7
PowerPoint 7,5 x 5,5
MS Word, ODT, RTF 5x4
EPUB 5x4

* Medidas en pulgadas

Grafico con tamaño predeterminado (9 x 5”)

Grafico con tamaño al 50% (4.5 x 2.5”)

Grafico con tamaño 12 x 6.67”

Archivos exportados


Comparemos dos archivos exportados desde ggplot2:

font_prueba1_5x5_300.png

font_prueba1_10x10_300.png

Problemas con fuentes


Observamos que el tamaño del punto y la fuente parecen más pequeños en el gráfico de la derecha.

De hecho no son más pequeños, siguen teniendo el mismo tamaño en pulgadas, y como guardamos con la misma resolución (dpi = 300), tienen el mismo número de puntos (tamaño en pulgadas * 300).

La fuente del gráfico aparece más pequeña porque el segundo gráfico es más grande (3000x3000 frente a 1500x1500) y se reduce su aspecto para que el gráfico entre el espacio de la pantalla.

Diferencia en tamaño de fuentes


Además tenemos otro problema. De forma predeterminada, el tamaño de las fuentes de la configuración de theme() está definido en pts. (15 significa 15 puntos - pts-).

En cambio, en la capa geom_text(), el tamaño se define en mm, por lo que 15 es 15 mm.


¿Cuál es la relación entre pts y mm o in?

1 punto = 1/72 pulgadas

1 punto = 0,35 mm

Diferencia en tamaño de fuentes

Entonces si queremos que el texto tenga el mismo tamaño que el título, el tamaño en mm será 15 pt * 0,35 pt/mm = 5,25 mm

En ggplot, hay una constante definida para realizar la conversión, .pt = 2,845276. (1/.pt = 0,35). Podemos escribir .pt en la consola y mostrará su valor:

.pt
[1] 2.845276
1/.pt
[1] 0.3514598

Entonces para hacer la conversión:

de pt a mm : mm = pt / .pt -> 15 / 2,845276 = 5,27 mm

de mm a pt : pt = mm * .pt -> 5,27 * 2,845276 = 15 pts

Diferencia en tamaño de fuentes


pinguinos |>
    ggplot(aes(x = largo_pico_mm, 
               y = alto_pico_mm, 
               color = especie)) +
  geom_point() +
  geom_text(x = 45, 
            y = 20, 
            label = "Ejemplo font 15", 
            size = 15, 
            inherit.aes = FALSE) +
  labs(title = "Relación entre largo y altura de picos por especies") +
  theme(plot.title = element_text(size = 15))

Diferencia en tamaño de fuentes

pinguinos |>
    ggplot(aes(x = largo_pico_mm, 
               y = alto_pico_mm, 
               color = especie)) +
  geom_point()+
  geom_text(x = 45, 
            y = 20, 
            label = "Ejemplo font 15", 
            size = 15/.pt, 
            inherit.aes = FALSE) +
  labs(title = "Relación entre largo y altura de picos por especies") +
  theme(plot.title = element_text(size = 15))